/**
 * Run_nodes.java
 * ************************** 
 * @date Apr 15, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package infrastructureAPI.scripts.instanceTools;

import gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import infrastructureAPI.ConcreteRequester;
import infrastructureAPI.Requester;
import infrastructureAPI.APIMessages.ResponseMessage;
import infrastructureAPI.algorithms.instanceTools.RunNodes;
import infrastructureAPI.backendGate.APIRoutineFunctions;
import infrastructureAPI.backendGate.APIScript;
import infrastructureAPI.backendGate.GeneralScript;
import infrastructureAPI.backendGate.SharedAPIParameters;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Vector;

import messages.Message;

/**
 * Description: 
 *
 */
public class run_nodes extends GeneralScript{
	
	protected static int parameterIndex;
	protected static String subcloudName;
	protected static Integer subcloudNodesNumber;

	public run_nodes(String[] args){
		super(args);
	}

	/**
	 * @param args
	 */
	public static void excuteScript(String[] args) {
		int parametersNumber = 0;
		int optionalParameterIndex = 0;
		int parameterIndex = 0;
		String name = "node1";
		Integer subcloudNodesNumber = null;
		String subcloudName;
		ResponseMessage reply;
		String registeredServiceName = run_nodes.class.getSimpleName();
		System.out.println("REQUEST: "+registeredServiceName+" on thread: "+Thread.currentThread().getName());
				
		// Translation of the input arguments
		// -----------------------
		Vector<String> arguments = APIRoutineFunctions.convertToVector(args);
		
		// Check of optional parameters 
		// -----------------------
		if(arguments.contains(SharedAPIParameters.NODE_NAME_OPTION)){
			optionalParameterIndex = arguments.indexOf(SharedAPIParameters.NODE_NAME_OPTION);
			optionalParameterIndex++;
			name = ConcreteNodeDescriptor.adjustNodeName(arguments.get(optionalParameterIndex));
			optionalParameterIndex++;
		}else{
			try {
				name = InetAddress.getLocalHost().getHostName();
				name = ConcreteNodeDescriptor.adjustNodeName(name);
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Submitted request from node: "+name);
		
		APIScript runNodes = new RunNodes();
		
		// Mandatory parameters verification
		// -----------------------
		parametersNumber = APIRoutineFunctions.extractParametersNumber(arguments);
		if(parametersNumber < runNodes.mandatoryParametersNumber()){ 
//			runNodes = new RunNodes();
			System.out.println(runNodes.showUsage());
		}else{
			// Mandatory parameters composition
			// -----------------------
			parameterIndex = optionalParameterIndex;
			subcloudName = arguments.get(parameterIndex);
			subcloudNodesNumber =  new Integer(arguments.get((++parameterIndex)));

			try {
				// Retrieve local address
				// -----------------------
				InetAddress myAddress = null;
				try {
					myAddress = InetAddress.getLocalHost();
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}

				// Requester composition
				// ------------------------
				Requester nodeRequester = new ConcreteRequester(myAddress, 
						registeredServiceName, APIRoutineFunctions.RMIREGISTRY_STADARD_PORT);

				// Instantiation of the APIScript: RunNodes
				// --------------------------
				runNodes = new RunNodes(subcloudName, subcloudNodesNumber, nodeRequester);

				// ResponseReceiver initialization
				// -----------------------
				APIRoutineFunctions.initializeResponseReceiver(myAddress, registeredServiceName); // <-- WARNING (*1)

				// RequestMessage: Marshaling
				// --------------------------
				Message msg = APIRoutineFunctions.marshal(myAddress, name, runNodes);

				// RequestMessage: Delivering
				// -----------------------
				System.out.println("Delivering message: "+msg+"\nto "+name);
				boolean ok = true;
				ok = APIRoutineFunctions.deliverMessage(msg, name); // <-- WARNING (*1) 
				if(ok == true){			
					System.out.println("SUCCESSFUL!\n"+
							"Request "+ runNodes.getClass().getSimpleName()+" " +
							"delivered with message: "
							+msg);
				}else{
					System.out.println("Problem in delivering: "+msg);
				}

			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		
		/*
		 * Note (*1): it's possible that the delivering and all the middle
		 * steps finishes before the ResponseReceiver initialization??
		 * Do we require a fork() of the current thread ???
		 */
		
		// Waiting for incoming replies:
		// --------------------------
		System.out.println("Waiting for answers: ");
		int expectedReplies = subcloudNodesNumber.intValue();
		Vector<NodeDescriptor> actualSubcloud = new Vector<NodeDescriptor>(expectedReplies);
		reply = APIRoutineFunctions.receiveResponse();
		System.out.println("Accepted request. Received reply: "+reply);
		for(int i=0; i < expectedReplies ; i++){
			reply = APIRoutineFunctions.receiveResponse();
			System.out.println("Received reply:\n"+i+") "+reply);
			actualSubcloud.add(reply.getSender());
		}
		
		System.out.println("SUBLCOUD formed by nodes:");
		for(NodeDescriptor n : actualSubcloud){
			System.out.println("> "+n);
		}
		
		// ResponseReceiver disconnection
		// -----------------------
		System.out.println("Thread "+Thread.currentThread().getName() + " terminating...");
		APIRoutineFunctions.disconnectReceiver();
		
	}

	public static void main(String[] args){
		@SuppressWarnings("unused")
		run_nodes runner = new run_nodes(args);
	}
	
	@Override
	public void checkNotGeneralOptionalParameters() {
		// nothing to do. This script may accept 
		// any but the shared optional parameters. 
	}

	@Override
	public void composeMandatoryParameters() {
		parameterIndex = optionalParameterIndex;
		subcloudName = arguments.get(parameterIndex);
		subcloudNodesNumber = new Integer(arguments.get((++parameterIndex)));		
	}

	@Override
	public APIScript instantiateAPIScript() {
		return new RunNodes(subcloudName, subcloudNodesNumber, requester);
	}

	@Override
	public boolean verifyMandatoryParameters() {
		int parametersNumber = APIRoutineFunctions.extractParametersNumber(arguments);
		RunNodes runNodes = new RunNodes();
		boolean ret = true;
		if(parametersNumber < runNodes.mandatoryParametersNumber()){ 
			System.out.println(runNodes.showUsage());
			ret = false;
		}
		return ret;
		
	}

	@Override
	public void waitingIncomingResposes() {
		// the first response means that a node collected the request
		ResponseMessage firstAnswer = waitingOneIncomingResponse();
		System.out.println("Response of request submission returning from "+
				firstAnswer.getSender());
		// we will wait for the other nodes responses
		List<ResponseMessage> responses = waitingIncomingResponses(subcloudNodesNumber.intValue());
		int size =responses.size();
		System.out.println("SUBLCOUD formed by "+size+" nodes:");
		for(int i=0; i< size; i++){
			ResponseMessage response = responses.get(i);
			System.out.println(i+") "+response.getSender());
		}
	}

}
